﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using FluentResourceMapping.AutoMapping;
using FluentResourceMapping.Util;

namespace FluentResourceMapping
{
    public class ResourceMappingModel
    {
        private readonly IList<ResourceMap> _resourceMaps = new List<ResourceMap>();

        private readonly IList<AutoResourceMappingModel> _autoResourceMappingModels =
            new List<AutoResourceMappingModel>();

        private TypeResourceMapping FindManualMapping(Type type)
        {
            if (type == null)
                return null;

            if (!_resourceMaps.Any(o => o.GetResourceMapping().MappingType == type))
            {
                return FindManualMapping(type.BaseType); // TODO: e interfaces?
            }

            return _resourceMaps.SingleOrDefault(o => o.GetResourceMapping().MappingType == type).GetResourceMapping();
        }

        private TypeResourceMapping FindAutoMapping(Type type)
        {
            foreach (AutoResourceMappingModel autoResourceMappingModel in _autoResourceMappingModels)
            {
                if (autoResourceMappingModel.Maps(type))
                {
                    return autoResourceMappingModel.GetMapping(type);
                }
            }

            return null;
        }

        public TypeResourceMapping FindMapping(Type type)
        {
            var mapping = FindManualMapping(type);

            if (mapping == null)
                return FindAutoMapping(type);

            return mapping;
        }

        public TypeResourceMapping FindMapping<T>()
        {
            return FindMapping(typeof (T));
        }

        public string GetResource(Type type, string propertyName)
        {
            TypeResourceMapping mapping = FindMapping(type);

            if (mapping == null) return null;

            return mapping.GetResource(propertyName);
        }

        public string GetResourceFor<T>(System.Linq.Expressions.Expression<Func<T, object>> memberExpression)
        {
            TypeResourceMapping mapping = FindMapping(typeof (T));

            //TODO: Improve this method

            dynamic strange = memberExpression;
            string name = strange.Body.Member.Name;

            return mapping.GetResource(name);
        }

        public void AddMappingsFromAssembly(Assembly assembly)
        {
            foreach (ResourceMap map in assembly.GetTypes().OfType<ResourceMap>())
            {
                AddMapping(map);
            }
        }

        public virtual void AddMapping(ResourceMap resourceMap)
        {
            _resourceMaps.Add(resourceMap);
        }

        public virtual void AddMapping(AutoResourceMappingModel autoResourceMappingModel)
        {
            _autoResourceMappingModels.Add(autoResourceMappingModel);
        }

        public virtual void AddMapping(Type type)
        {
            var mapping = type.InstantiateUsingParameterlessConstructor();

            if (mapping is ResourceMap)
            {
                AddMapping((ResourceMap) mapping);
            }
            else if (mapping is AutoResourceMappingModel)
            {
                AddMapping((AutoResourceMappingModel) mapping);
            }
            else
            {
                throw new InvalidOperationException("Unsupported mapping type '" + type.FullName + "'");
            }
        }
    }
}